/**
 * ide web端通过ws调用service来实现在vsc插件端的业务
 * 插件只对ide提供服务,至于业务怎么组合,由ide端来决定
 * service.js是所有服务的入口文件
 */

const path = require('path');
const fs = require('fs');
const vscode = require('vscode');
var less = require('less');


const { Defer, info, AsyncTaskQueue, travelDirAsync, errorInfo, asyncSleep } = require('../utils.js');
const { contributeServicesForSavePoint } = require('./savePointFile.js');
const { contributeServicesForEditor } = require('./editor.js');
const { frameworkDir, workSpacePath } = require('../base.js')

const statusBarHandler = {
    statusBarItem: vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1),

    activeConfigId: null,
    activeConfigIdText() {
        return this.activeConfigId ? `当前选中组件：${this.activeConfigId}` : '';
    },

    actionMsg: null,
    actionMsgText() {
        return this.actionMsg ? `    正在${this.actionMsg},请稍后...` : '';
    },

    clipboardItemName: null,
    clipboardItemNameText() {
        return this.clipboardItemName ? `    剪贴板：${this.clipboardItemName}` : '';
    },

    showStatusMsg() {
        const text = this.activeConfigIdText() + this.actionMsgText() + this.clipboardItemNameText();
        this.statusBarItem.text = text;
        this.statusBarItem.show();
    }
}


/**
 * 从reply方法中获取设计文件的路径
 * @param {*} reply
 */
function getRelaPathFromWebView(reply) {
    //获取mxDev参数
    let mxDev = reply['__mxDev'];
    /**
     * 取得被编辑的vue文件地址,(这里可能仅仅是编辑其内部一个组件的属性)
     * _mx_demo是默认值
     */
    let targetRelaPath;
    mxDev && (targetRelaPath = mxDev.target);
    !targetRelaPath && (targetRelaPath = Symbol.for('demo'));

    return targetRelaPath;
}

/**
 * 会由插件实际上的入口文件openWebview.js在插件初始化的时候调用
 * @param {*} subscriptions
 * @param {*} panelHandler 
 * @returns 
 */
function createServices(subscriptions, panelHandler) {

    const w = vscode.window, ws = vscode.workspace;


    const { ServiceHandler } = require('../utils.js');
    /**
     * 创建servicehandler,用来初始化和包装service方法
     */
    const serviceHandler = new ServiceHandler();

    //设置AsyncTaskQueue静态属性
    AsyncTaskQueue.warn = w.showWarningMessage.bind(w);

    /**
     * 绑定在context中,供其他模块使用
     */
    serviceHandler.context.getRelaPathFromWebView = getRelaPathFromWebView;
    serviceHandler.context.statusBarHandler = statusBarHandler;
    serviceHandler.context.panelHandler = panelHandler;
    serviceHandler.context.subscriptions = subscriptions;

    /**
    * 当关闭正在编辑的文档的时候,结束编辑请求
    * 这个只有手动打开的文件关闭时才可以响应
    * 同样判断不出来的还有docuemnt.isclose
    * @deprecated
    */
    ws.onDidCloseTextDocument(textDocument => {
        // info('----close-----', textDocument.fileName, editingEditor);
        // if (editingEditor && editingEditor.document === textDocument) {
        //     closeLastEditing(false);
        // }
    });

    /**
     * 全局剪贴板中的内容
     */
    let clipboardItem;
    /**
     * 注册同步方法
     */
    serviceHandler.registerService({
        infoMessage() {
            const args = [...arguments];
            const reply = args.pop();
            w.showInformationMessage(args.join(''));
            reply();
        },

        errorMessage() {
            const args = [...arguments];
            const reply = args.pop();
            w.showErrorMessage(args.join(''));
            reply();
        },

        warningMessage() {
            const args = [...arguments];
            const reply = args.pop();
            w.showWarningMessage(args.join(''));
            reply();
        },

        /**
         * 从web端的调用
         */
        showStatusMsg(activeConfigId, reply) {
            statusBarHandler.activeConfigId = activeConfigId;
            statusBarHandler.showStatusMsg();
            reply && reply();
        },

        /**
         * 设置剪贴板
         * @param {{}} item
         * @param {String} itemName
         */
        setClipboard(item, itemName, reply) {
            info(itemName, item);
            clipboardItem = item;
            statusBarHandler.clipboardItemName = itemName;
            statusBarHandler.showStatusMsg();
            reply && reply();
        },

        /**
         * 获取剪贴板内容
         */
        getClipboard(reply) {
            reply(clipboardItem);
            reply();
        }
    });

    const frameworkSuffix = '.framework.js';

    serviceHandler.regsiterAsyncService({
        /**
        * vscode输入框,这个视觉效果很差,不建议使用
        * 这个功能因为要知道异步结果,所以也注册为异步方法
        * @param {*} value   值 
        * @param {*} prompt  提示
        * @param {*} reply 
        */
        async inputBox(value, prompt, reply) {
            //没用await,好像也不太对
            const t = w.showInputBox({ value, prompt });
            if (reply) {
                const defer = new Defer();
                t.then(res => {
                    res && reply(res + '');
                    reply();
                    defer.resolve(res);
                });
                return defer.promise;
            }
        },

        /**
         * 获取workspace中用户自定义framework的所有名称
         * @param {String} ns
         *    命名空间名称,目前只有v2(vue2.x)
         */
        async getFrameworkNames(ns, reply) {
            const p = path.join(frameworkDir, ns);
            const fs = [];
            try {
                await travelDirAsync(p, f => {
                    if (f.endsWith(frameworkSuffix)) {
                        f = path.relative(p, f);
                        fs.push(f.substring(0, f.length - frameworkSuffix.length));
                    }
                });
                fs.length && reply(fs);
            } catch (e) {
                /**
                 * 可能是.mxDev\framework\v2不存在造成的
                 */
                errorInfo(`遍历目录:${p}出现错误!`, e);
            }
            reply();
        },

        /**
         * 重启webview
         */
        async restartWebView(reply) {
            const targetRelaPath = getRelaPathFromWebView(reply);
            reply();

            w.showInformationMessage(`正在重新打开设计页面:${targetRelaPath.toString()}`);
            /**
             * @type {vscode.WebviewPanel}
             */
            let panel = panelHandler.getPanel(targetRelaPath);
            if (panel) {
                panel.dispose();

                let i = 0
                do {
                    await asyncSleep(100);
                } while ((panel = panelHandler.getPanel(targetRelaPath) && i++ < 50));

                await asyncSleep(200);

                if (!panel) {
                    //info('旧的panel已经销毁', panel);
                } else {
                    //超过一定时间无法关闭,抛出异常
                    throw new Error(`文件${targetRelaPath.toString()}对应的编辑器无法关闭!`);
                }
            }

            //也支持demo页面的重启
            const newuri = typeof targetRelaPath === 'string' ?
                vscode.Uri.parse('file:/' + path.join(workSpacePath, targetRelaPath)) : undefined;
            panel = await vscode.commands.executeCommand("mxdev.openAndDev", newuri);
        },

        /**
         * 编译less内容为css
         * @param {String} lessContent
         */
        async compileLess(lessContent, reply) {
            if (lessContent && (lessContent = lessContent.trim())) {
                let cssContent, errorMessage;
                try {
                    cssContent = await new Promise((resolve, reject) => {
                        less.render(lessContent, function (e, css) {
                            if (e) {
                                reject(e);
                            } else {
                                resolve(css);
                            }
                        });
                    });
                } catch (e) {
                    errorMessage = e.message;
                    w.showErrorMessage(errorMessage);
                }
                reply({ cssContent, errorMessage });
            }

            reply();
        },

        test0() {
            w.showInformationMessage("仅在内部测试使用");
        }
    });

    /**
     * 注册savePoint提供的service方法
     */
    try {
        contributeServicesForSavePoint(serviceHandler);
        contributeServicesForEditor(serviceHandler);
    } catch (e) {
        console.error(e);
    }

    return serviceHandler.services;
}


module.exports = createServices;